/*
 * Copyright 2005-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package asphalt2.processor
{
	import asphalt2.control.IControl;
	
	import flash.events.Event;
	import flash.utils.describeType;
	/**
	 * プロセッサー基本クラス
	 * イベントハンドル後、executeメソッドを呼び出します。
	 * @author c9katayama
	 **/
	public class BaseProcessor implements IProcessor
	{
		protected var _control:IControl;
		protected var _event:Event;
		//TODO 不要？		
		protected var _processing:Boolean = false;
		
		protected function voidFunction():void{};

		public function get control():IControl
		{
			return _control;
		}
		/**
		 * 発生したイベントを返します。
		 */
		public function get event():Event
		{
			return _event;
		}
		
		public function BaseProcessor(control:IControl)
		{
			this._control = control;
		}
		public function getHandler():Function{
			return handle;
		}
		public function handle(event:Event=null):void
		{
			if(!_processing){
				try{
				_processing = true;
				_event = event;
				doProcess(event);
				}finally{
					_processing = false;
				}		
			}
		}
		public function doProcess(event:Event):void{
			invokeControl("execute",this);
		}
		
		/**
		 * コントロールの呼び出し
		 * 非同期メソッドの存在を調べ、あった場合はそのメソッドを、ない場合は
		 * 同期メソッドを呼び出します。
		 */
		protected function invokeControl(controlFunctionName:String,nextFunctionOwner:Object=null,nextFunctionName:String=null,args:Array=null):void{
			
			var asyncFunctionName:String = "async" + controlFunctionName.charAt(0).toUpperCase()+controlFunctionName.substring(1); 
			
			if(Object(_control).hasOwnProperty(asyncFunctionName)){
				var nextFunction:Function = (nextFunctionName==null) ? voidFunction : nextFunctionOwner[nextFunctionName];
				var asyncArgs:Array = [nextFunction];
				if(args != null){
					asyncArgs = asyncArgs.concat(args);
				}
				invokeFunction(_control,asyncFunctionName,asyncArgs);
			}else if(Object(_control).hasOwnProperty(controlFunctionName)){
				var nextFunctionArg:Object = invokeFunction(_control,controlFunctionName,args);
				if(nextFunctionName != null){
					invokeFunction(nextFunctionOwner,nextFunctionName,[nextFunctionArg]);
				}
			}else{
				if(nextFunctionName != null){
					invokeFunction(nextFunctionOwner,nextFunctionName,null);
				}
			}
		}
		/**
		 * 引数を調べ引数の数を調整し、Function呼び出しを行います。
		 */
		protected function invokeFunction(target:Object,methodName:String,args:Array):Object{
			var params:XMLList = describeType(target).method.(@name==methodName).parameter;			
			if(params.length()==0){
				return target[methodName]();
			}else{
				var adjustArgs:Array = new Array();
				for(var i:Number=0;i < params.length();i++){
					if(args != null && i < args.length){
						adjustArgs[i] = args[i];
					}else{
						if(params[i].@optional=="true"){
							adjustArgs[i] = null;
						}else{
							//TODO 数字とかの場合デフォルト値?
							adjustArgs[i] = null;
						}
					}
				}
				var f:Function = target[methodName];
				return f.apply(target,adjustArgs);
			}
		}
		protected function dispose():void{
			_control = null;
			_event = null;
		}
	}
}